﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mbs.Collections;
using Mbs.Reflection;
namespace Mbs.Messaging.Imp
{
    [Serializable]
    [DebuggerTypeProxy(typeof(DebuggerEnumerableView<>))]
    public class MessageRepository : MarshalAndDisposableObject, IMessageRepository
    {
        private IMap<Key, ISubject> Table;

        public string Name { get; set; }
        public ISubjectBuilder Builder { get; private set; }
        public IMessageListnerManager ListnerManager { get; private set; }
        public IDelegateInvoker DelegateInvoker { get; private set; }


        public MessageRepository()
            : this(new Map<Key, ISubject>(), new SubjectBuilder(), new MessageListnerManager(), Mbs.Messaging.Imp.DelegateInvoker.Create())
        {
        }

        public MessageRepository(IMap<Key, ISubject> subjects, ISubjectBuilder builder, IMessageListnerManager hookManager, IDelegateInvoker delegateInvoker)
        {
            if (subjects == null)
                throw new ArgumentNullException("subjects");
            if (builder == null)
                throw new ArgumentNullException("builder");
            if (hookManager == null)
                throw new ArgumentNullException("hookManager");
            if (delegateInvoker == null)
                throw new ArgumentNullException("delegateInvoker");

            Table = subjects;
            Builder = builder;
            ListnerManager = hookManager;
            DelegateInvoker = delegateInvoker;
        }
       
        public int Count
        {
            get { return Table.Count; }
        }

        public bool ContainSubject(Type type)
        {
            var key = Key.Make(type);
            return Table.ContainsKey(key);
        }

        public bool ContainSubject(string topic, Type type)
        {
            var key = Key.Make(type, topic);
            return Table.ContainsKey(key);
        }

        public bool ContainSubject<TMessage>()
        {
            var key = Key.Make<TMessage>();
            return Table.ContainsKey(key);
        }

        public bool ContainSubject<TMessage>(string topic)
        {
            var key = Key.Make<TMessage>(topic);
            return Table.ContainsKey(key);
        }

        public ISubject<TMessage> Subject<TMessage>()
        {
            var key = Key.Make<TMessage>();
         
            return Table.GetOrAdd(key, () =>
                {
                    var s = Builder.Build<TMessage>(key.ToString());
                    s.Subscriber.ListnerManager = ListnerManager;
                    s.Executor.ListnerManager = ListnerManager;
                    s.Executor.DelegateInvoker = DelegateInvoker;
                    return s;
                }) as ISubject<TMessage>;
        }

        public ISubject<Topic<TMessage>> Subject<TMessage>(string topic)
        {
            var key = Key.Make<Topic<TMessage>>(topic);
            ISubject result = null;
            ISubject<Topic<TMessage>> subject = null;

            if (Table.TryGetValue(key, out result))
                return result as ISubject<Topic<TMessage>>;

            subject = Builder.Build<Topic<TMessage>>(key.ToString());
            subject.Subscriber.ListnerManager = ListnerManager;
            subject.Executor.ListnerManager = ListnerManager;
            subject.Executor.DelegateInvoker = DelegateInvoker;
            Table[key] = subject;

            return subject;
        }


        public void RegisterSubject(string topic, Type type, ISubject subject)
        {
            if (string.IsNullOrEmpty(topic) )
                throw new ArgumentNullException("topic");
            if (type == null)
                throw new ArgumentNullException("type");
            if (subject == null)
                throw new ArgumentNullException("subject");

            var key = Key.Make(type, topic);
            if (Table.ContainsKey(key))
                throw new Exception("has repeat " + key.ToString());
            Table[key] = subject;
        }
       

        public ISubject this[string topic, Type type]
        {
            get
            {
                var key = Key.Make(type, topic);
                ISubject result = null;

                if (Table.TryGetValue(key, out result))
                    return result;

                result = Builder.Build(type, key.ToString());
                result.GetProperty("Subscriber").SetProperty("ListnerManager", ListnerManager);
                result.GetProperty("Executor").SetProperty("ListnerManager", ListnerManager);
                result.GetProperty("Executor").SetProperty("DelegateInvoker", DelegateInvoker);
                Table[key] = result;

                return result;
            }
        }

        public ISubject this[Type type]
        {
            get
            {
                var key = Key.Make(type);
                ISubject result = null;

                if (Table.TryGetValue(key, out result))
                    return result;

                result = Builder.Build(type, key.ToString());
                result.GetProperty("Subscriber").SetProperty("ListnerManager", ListnerManager);
                result.GetProperty("Executor").SetProperty("ListnerManager", ListnerManager);
                result.GetProperty("Executor").SetProperty("DelegateInvoker", DelegateInvoker);
                Table[key] = result;

                return result;
            }
        }

        public void Remove<TMessage>()
        {
            Remove<TMessage>(null);
        }

        public void Remove<TMessage>(string topic)
        {
            var key = Key.Make<TMessage>(topic);
            if (Table.ContainsKey(key))
            {
                var subject = Table[key];
                Table.Remove(key);
                subject.Close();
            }
        }

        public void Remove(Type type)
        {
            Remove(null, type);
        }

        public void Remove(string topic, Type type)
        {
            var key = Key.Make(type, topic);
            if (Table.ContainsKey(key))
            {
                var subject = Table[key];
                Table.Remove(key);
                subject.Close();
            }
        }

        public void Clear()
        {
            var tmpItems = Table.Values;
            

            if (tmpItems != null && tmpItems.Count() > 0)
                tmpItems.Foreach(item => item.Close());

            Table.Clear();
        }

        public void Dispose()
        {
            Clear();
        }

        public IEnumerator<KeyValuePair<Key, ISubject>> GetEnumerator()
        {
            return Table.GetEnumerator();
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return Table.GetEnumerator();
        }

       
    }
}
